達文西畫完一張臉的透視,不會就此收筆
他會退後三步,看這張臉跟整幅畫其他物件的消失點,是不是還落在同一個點上單一物件的結構畫對了,不代表整幅畫的空間邏輯是一致的
兩個物件各自的透視都沒問題,擺在一起卻可能互相矛盾模組二的五個警訊也是一樣,「各自修對了,不代表放在同一個系統裡,還撐得住彼此」
Day 11 拆過一次 PaymentMethod,把「一定能做到」的 Charge,留在父類別
把「不是每種付款方式都做得到」的 Refund,搬進 RefundablePaymentMethod 這個子類別
這解決了里氏替換的問題(子類別不會再對著承諾的方法丟例外)
但退後三步看整個系統,會發現另一個地方,還藏著一組跟這個結構緊密相關的判斷:
// 🔴 選擇該用哪一種付款方式的邏輯,散落在下單流程裡
public PaymentMethod ResolvePaymentMethod(string paymentType)
{
switch (paymentType)
{
case "CreditCard": return new CreditCardPayment();
case "CashOnDelivery": return new CashOnDeliveryPayment();
default: throw new ArgumentException("未知的付款方式");
}
}
這個 switch,看起來很眼熟,跟 Day 09 的通知管道是同一種形狀
先別急著動手拆,這個 switch 有幾個跟 Day 09 通知範例不一樣的地方:
new 哪一個具體付款方式」回頭看 Day 09 的結論:switch 用在工廠裡,本身就是多型結構的入口,不是問題
真正該處理的,不是這個 switch,是它產出的物件 CreditCardPayment、CashOnDeliveryPayment
彼此之間該不該共用同一個父類別的所有方法,這正是 Day 11 已經處理過的里氏替換問題
結構對不對,要看兩層:
兩層各自檢查,結論才完整
多型的成本,是多幾個類別、多一層間接
如果某個型別判斷,已知只有兩三種、而且幾乎不會再增加,一段清楚的 if/else 有時反而比硬拆出五個只有一行程式碼的類別,更容易讀
判斷的關鍵不是「有沒有型別判斷」,是:
「這個判斷,未來會不會反覆長出新的分支、會不會在超過一個地方重複出現」
只有符合這兩個條件之一,才值得換成多型
明天我們進入模組三 「濕壁畫的灰泥」
一旦抹上牆,就有了乾燥的時限
變更的妨礙者,正式開工